package com.example;

// 测试非逃逸对象的栈上分配
// 运行参数：-server -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations
// 运行参数说明：
// -server: 以Server模式运行JVM，因为在Server 模式下，才可以启用逃逸分析。
// -Xmx100m: 设置最大堆内存为100MB，这个空间很小，可以预计，如果对象在堆上分配，那么必然导致大量的GC。
// -Xms100m: 设置最小堆内存为100MB
// -XX:+DoEscapeAnalysis: 开启逃逸分析
// -XX:+PrintGC: 打印GC日志
// -XX:-UseTLAB: 禁用线程本地分配缓冲区（关于TLAB：由于堆内存是线程共享的，所以在给对象分配内存的时候可能会出现线程冲突，
//                                            为了避免这种情况，可以给每个线程在堆上先预留一块内存空间，也就是TLAB，
//                                            这样，在在TLAB没用完之前，给对象分配内存都先从TLAB上分配，就不会出现线程冲突，也就不需要进行同步了，可以提高效率）
// -XX:+EliminateAllocations 打开标量替换（JDK8种默认就是打开的），标量替换允许将对象打散分配在栈上。关于标量替换，举个例子：下面下面的allocate()方法，
//                           由于user对象不会发生逃逸，所以JVM可能会使用标量替换来进行优化，也就是将id、username这两个字段作为独立的局部变量分配在栈上。
//
// 运行程序，发现没有打印出任何GC日志，说明在执行过程中， User 对象的分配过程被优化。
// 如果关闭逃逸分析或者标量替换中任何一个，再次执行程序，就会看到大量的GC 日志，说明栈上分配依赖逃逸分析和标量替换实现。
public class OnStackAllocation {

    public static class User{
        private Integer id;
        private String username;
    }

    public static void allocate(){
        // 由于user对象不会逃逸出allocate()方法
        // 所以它可能会被进行标量替换、栈上分配之类的优化
        User user = new User();
        user.id = 1;
        user.username = "zhangsan";
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100000000; i++) {
            allocate();
        }
    }
}
